/***** BEGIN LICENSE BLOCK *****
 Version: MPL 1.1
 
 The contents of this file are subject to the Mozilla Public License Version 
 1.1 (the "License"); you may not use this file except in compliance with 
 the License. You may obtain a copy of the License at 
 http://www.mozilla.org/MPL/
 
 Software distributed under the License is distributed on an "AS IS" basis,
 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 for the specific language governing rights and limitations under the
 License.
 
 The Original Code is GureGipuzkoa Extension.
 
 The Initial Developer of the Original Code is Asier Sarasua Garmendia.
 Portions created by the Initial Developer are Copyright (C) 2009
 the Initial Developer. All Rights Reserved.
 
 Contributor(s):
    Asier Sarasua Garmendia <asier.sarasua@gmail.com>
    Julen Ruiz Aizpuru <julenx@gmail.com>
    Juanan Pereira <juanan@diariolinux.com>
 
 ***** END LICENSE BLOCK *****/


const gureGipuzkoaUtils = {
  prefs: null,

  prefManager: Components.classes["@mozilla.org/preferences-service;1"]
                         .getService(Components.interfaces.nsIPrefBranch),

  onStartup: function() {
    this.prefs = Components.classes["@mozilla.org/preferences-service;1"]
                           .getService(Components.interfaces.nsIPrefService)
                           .getBranch("guregipuzkoa.");
    this.prefs.QueryInterface(Components.interfaces.nsIPrefBranch2);

    // Load button in toolbar
    if (navigator.preference('extensions.guregipuzkoa.init')) {
      var initPref = Components.classes["@mozilla.org/extensions/manager;1"]
                               .getService(Components.interfaces.nsIExtensionManager)
                               .getInstallLocation(gureGipuzkoa.uid)
                               .getItemLocation(gureGipuzkoa.uid);
      initPref.append("defaults");
      initPref.append("preferences");
      initPref.append("init.js");
      initPref.remove(false);

      try {
        var firefoxnav = document.getElementById("nav-bar");
        var curSet = firefoxnav.currentSet;
        if (curSet.indexOf("ggButton") == -1) {
          var set;
          // Place the button before the urlbar
          if (curSet.indexOf("urlbar-container") != -1) {
            set = curSet.replace(/urlbar-container/, "ggButton,urlbar-container");
          } else {
            set = curSet + ",ggButton";
          }
          firefoxnav.setAttribute("currentset", set);
          firefoxnav.currentSet = set;
          document.persist("nav-bar", "currentset");
          // If you don't do the following call, funny things happen
          try {
            BrowserToolboxCustomizeDone(true);
          }
          catch (e) { }
        }
      }
      catch(e) { }

      //Set the addon's language
      var bdls = Components.classes["@mozilla.org/intl/stringbundle;1"]
                                                 .getService(Components.interfaces.nsIStringBundleService);
      var bdl = bdls.createBundle("chrome://global/locale/intl.properties");
      var h = bdl.GetStringFromName("general.useragent.locale");
      switch(h) {
      case "eu":
      case "eu-ES":
      case "eu-FR":
        gureGipuzkoaUtils.prefManager.setCharPref("guregipuzkoa.lang.default", "eu");
        break;
      case "es-ES":
      default:
        gureGipuzkoaUtils.prefManager.setCharPref("guregipuzkoa.lang.default", "es");
        break;
      }

      // Open help in the first launch
      var t = setTimeout("gureGipuzkoa.openHelp('chrome://guregipuzkoa/locale/help/help.html')", 1000);
    }
  },

  onLoad: function() {
    //Set fixed width for the sidebar
    var ggSb = gureGipuzkoaUIUtils.mainWindow.document.getElementById("sidebar-box");
    if (ggSb.width < "320") {
      ggSb.width="320";
    }


    // Set XUL elements state
    if (gureGipuzkoaUtils.prefManager.getBoolPref("guregipuzkoa.title.checked")) {
      document.getElementById("gg-check-title").setAttribute("checked", "true");
    }
    if (gureGipuzkoaUtils.prefManager.getBoolPref("guregipuzkoa.tag.checked")) {
      document.getElementById("gg-check-tag").setAttribute("checked", "true");
    }
    if (gureGipuzkoaUtils.prefManager.getBoolPref("guregipuzkoa.mine.selected")) {
      document.getElementById("gg-search-mine").setAttribute("selected", "true");
      document.getElementById("gg-search-everyone").setAttribute("selected", "false");
    } else {
      document.getElementById("gg-search-mine").setAttribute("selected", "false");
      document.getElementById("gg-search-everyone").setAttribute("selected", "true");
    }

    // Set initial login state
    var hostname = 'chrome://guregipuzkoa';
    var formSubmitURL = null;
    var httprealm = "Gure Gipuzkoa Login";
    var aUsername;

    try {
      // Get Login Manager 
      var myLoginManager = Components.classes["@mozilla.org/login-manager;1"]
                                     .getService(Components.interfaces.nsILoginManager);

      // Find users for the given parameters
      var logins = myLoginManager.findLogins({}, hostname, formSubmitURL, httprealm);

      if (myLoginManager.countLogins(hostname, formSubmitURL, httprealm) != 0) {
        // Function to switch login buttons and label
        ggAccounts.switchLogin(logins[0].username, "true");
      }
    }
    catch(ex) {
      // This will only happen if there is no nsILoginManager component class
    }

    // Retrieve and store API key for later use
    gureGipuzkoa.apiKey = gureGipuzkoaUtils.prefManager.getCharPref("guregipuzkoa.user.apikey");

    // Check login information in gureGipuzkoa
    ggAccounts.checkLogin();

    // Retrieve first token
    gureGipuzkoa.getToken();

    // Retrieve license dropdown information
    gureGipuzkoa.apiCall(
      {method: 'gg.photos.licenses.getInfo'},
      function(xhr) {
        var licenses = xhr.responseXML.getElementsByTagName("license");
        var licenseMenupopup = document.getElementById("gg-up-license-popup");
        for (var i = 0; i < licenses.length; i++) {
          var menuItem = document.createElement("menuitem");
          var licenseId = licenses[i].attributes[0].value;
          var licenseName = licenses[i].attributes[1].value;
          var licenseURL = licenses[i].attributes[2].value;
          menuItem.setAttribute("value", licenseId);
          menuItem.setAttribute("label", gureGipuzkoaStrUtils.parseNameFromURL(licenseURL));
          menuItem.setAttribute("tooltiptext", licenseName);
          licenseMenupopup.appendChild(menuItem);
        }
        var licenseMenulist = document.getElementById("gg-up-license");
        var defaultLicense = gureGipuzkoaUtils.prefManager.getCharPref("guregipuzkoa.license.default");
        licenseMenulist.selectedIndex = gureGipuzkoaUIUtils.getIndexByValue(licenseMenulist, defaultLicense);
      },
      function() {});

    // Set license link
    var licenseLabel = document.getElementById("gg-up-license-label");
    licenseLabel.onclick = function() {
      var win = Components.classes['@mozilla.org/appshell/window-mediator;1']
                          .getService(Components.interfaces.nsIWindowMediator)
                          .getMostRecentWindow('navigator:browser');
      win.openUILinkIn(gureGipuzkoa.licenseURL, 'tab');
    }

    // Select default language 
    var langMenulist = document.getElementById("gg-up-lang");
    var defaultLang = gureGipuzkoaUtils.prefManager.getCharPref("guregipuzkoa.lang.default");
    // langMenulist.selectedIndex = gureGipuzkoaUIUtils.getIndexByValue(langMenulist, defaultLang);

  },

  onXULChange: function(el) {
    //Store XUL elements state in preferences
    if (el.id == "gg-check-title") {
      gureGipuzkoaUtils.prefManager.setBoolPref("guregipuzkoa.title.checked", !gureGipuzkoaUtils.prefManager.getBoolPref("guregipuzkoa.title.checked"));
    } 
    if (el.id == "gg-check-tag") {
      gureGipuzkoaUtils.prefManager.setBoolPref("guregipuzkoa.tag.checked", !gureGipuzkoaUtils.prefManager.getBoolPref("guregipuzkoa.tag.checked"));
    } 
    if (el.id == "gg-search-mine") {
      gureGipuzkoaUtils.prefManager.setBoolPref("guregipuzkoa.mine.selected", true);
    } 
    if (el.id == "gg-search-everyone") {
      gureGipuzkoaUtils.prefManager.setBoolPref("guregipuzkoa.mine.selected", false);
    } 
  },

  onShutdown: function() {

  },

  errorDumpXHR:
    function(responseXML, method) {
      var method = typeof(method) != 'undefined' ? method : 'upload';
      var error = responseXML.getElementsByTagName('err').item(0);
      var eCode = error.getAttribute('code');
      var eMsg = error.getAttribute('message');
      dump(method + ' failed: ' + eCode + ' ' + eMsg + '\n');
    },

}


const ggPrefUtils = {
  onLoad:
    function() {
    // Retrieve license dropdown information
    gureGipuzkoa.apiCall(
      {method: 'gg.photos.licenses.getInfo'},
      function(xhr) {
        var licenses = xhr.responseXML.getElementsByTagName("license");
        var licenseMenupopup = document.getElementById("gg-prefs-license-popup");
        for (var i = 0; i < licenses.length; i++) {
          var menuItem = document.createElement("menuitem");
          var licenseId = licenses[i].attributes[0].value;
          var licenseName = licenses[i].attributes[1].value;
          var licenseURL = licenses[i].attributes[2].value;
          menuItem.setAttribute("value", licenseId);
          menuItem.setAttribute("label", gureGipuzkoaStrUtils.parseNameFromURL(licenseURL));
          menuItem.setAttribute("tooltiptext", licenseName);
          licenseMenupopup.appendChild(menuItem);
        }
        var licenseMenulist = document.getElementById("gg-prefs-license-list");
        var defaultLicense = gureGipuzkoaUtils.prefManager.getCharPref("guregipuzkoa.license.default");
        licenseMenulist.selectedIndex = gureGipuzkoaUIUtils.getIndexByValue(licenseMenulist, defaultLicense);
      },
      function() {});
  },

}


const gureGipuzkoaStrUtils = {
  strBundle:
    null,

  reHTML:
    /<\/?[^>]+(>|$)/g,

  onLoad:
    function() {
      // Initialize string bundle
      this.strBundle = document.getElementById("gg-jslocale");
  },

  /* Constructs a human-readable license name
   * based on the URL of the license text.
   */
  parseNameFromURL:
    function(url) {
      if (url.indexOf("creativecommons") != -1) {
        var licenseName = "CC";
        var parts = url.split("licenses")[1].split("/");
        licenseName += " " + parts[1] + " " + parts[2];
        return licenseName;
      } else if (url.indexOf("license-NKCR") != -1) {
        return this.strBundle.getString("LicenseUnknown");
      } else {
        return "Copyright";
      }
  },

  /* Creates an string object based on character codes
   */
  splitASCII:
    function(str) {
      var rv = '';
      for (var i = 0; i < str.length; ++i) {
        var w = str.charCodeAt(i);
        dump(w);
        dump(w & 0xff);
        dump((w >> 8) & 0xff);
        rv += String.fromCharCode(w & 0xff, (w >> 8) & 0xff);
      }
      return rv;
  },

  /* Returns a string with the URI representing the file
   */
  getURIFromFile:
    function(file) {
      var ioService = Components.classes['@mozilla.org/network/io-service;1']
                                .getService(Components.interfaces.nsIIOService);
      var fileProtocolHandler =
        ioService.getProtocolHandler("file")
                 .QueryInterface(Components.interfaces.nsIFileProtocolHandler);
      var uri = fileProtocolHandler.getURLSpecFromFile(file);
      return decodeURIComponent(uri);
  },

  /* Creates parameters for the upload
   */
  getUploadParams:
    function(file) {
      return {name: file.name,
              title_eu: file.title_eu,
              title_es: file.title_es,
              description_eu: file.desc_eu,
              description_es: file.desc_es,
              tags: file.tags,
              license_id: file.license,
              photographer: file.author,
              latitude: file.lat,
              longitude: file.lon,
              // FIXME: album_id is not yet supported by the API
              // album_id: file.album,
              date: this.formatDate(file.pdate)}
  },

  /* Removes HTML tags from a given text */
  removeTags:
    function(text) {
      return text.replace(this.reHTML, "");
  },

  /* Formats a given date string according to the API needs.
   * Input is in YYYY-MM-DD format
   * Output is in DD/MM/YYYY format
   */
  formatDate:
    function(dStr) {
      var pieces = dStr.split("-");
      return pieces[2] + "/" + pieces[1] + "/" + pieces[0];
    },
  
  stringToUTF8:
    function(aStr) {
       var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]
                    .createInstance(Components.interfaces.nsIScriptableUnicodeConverter);

       converter.charset = "utf-8" ;

       return converter.ConvertFromUnicode(aStr);
  },


}


const gureGipuzkoaUIUtils = {

  mainWindow: window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                    .getInterface(Components.interfaces.nsIWebNavigation)
                    .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
                    .rootTreeItem
                    .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                    .getInterface(Components.interfaces.nsIDOMWindow),

  prompt: Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
                    .getService(Components.interfaces.nsIPromptService),

  /* Adds a new tab with the given URL or reuses a tab when the URL is already open
   */
  useTab:
    function(url) {
      var found = false;
      var index = 0;
      var numTabs = gureGipuzkoaUIUtils.mainWindow.getBrowser().mTabContainer.childNodes.length;
      while (index < numTabs && !found) {
        var currentTab = gureGipuzkoaUIUtils.mainWindow.getBrowser().getBrowserAtIndex(index);
        var currentTabURI = currentTab.currentURI.spec;
        if (currentTabURI.indexOf("guregipuzkoa")!= -1) {
          // If open...
          currentTab.loadURI(url, null, null, null);
          found = true;
        }
       index++;
      }
      if (!found) {
        var currentTab = gureGipuzkoaUIUtils.mainWindow.getBrowser().addTab(url);
      }
      gureGipuzkoaUIUtils.mainWindow.getBrowser().selectedTab = currentTab;
    },

  /* Gets the index of a menuitem element according to its value
   */
  getIndexByValue:
    function(element, value) {
      if (element!=null){
        var selectedLang = element.getElementsByAttribute("value", value)[0];
        return element.getIndexOfItem(selectedLang);
     }
        return 0;
   },

  paginate:           //Manage the pagination
    function(page, pages, aPrev, aNext, aCurrent) {
      aCurrent.value = gureGipuzkoaStrUtils.strBundle.getString("Pages")+" "+page+"/"+pages;
      if(Number(page) > 1) {
        aPrev.disabled = false;
      } else {
        aPrev.disabled = true;
      }
      if(Number(page) < Number(pages)) {
        aNext.disabled = false;
      } else {
        aNext.disabled = true;
      }
    },

  /* Enables the EXIF button */
  enableEXIF:
    function() {
      var exifButton = document.getElementById("gg-up-exif-button");
      exifButton.disabled = false;
    },

  /* Disables the EXIF button */
  disableEXIF:
    function() {
      var exifButton = document.getElementById("gg-up-exif-button");
      exifButton.disabled = true;
    },

}


const ggExifUtils = {
  SOI_MARKER: 0xFFD8,  // start of image
  SOS_MARKER: 0xFFDA,  // start of stream
  EOI_MARKER: 0xFFD9,  // end of image
  APP1_MARKER: 0xFFE1, // start of binary EXIF data
  INTEL_BYTE_ORDER: 0x4949,

  exifDone: false,

  read16:
    function (data, offset, swapbytes) {
      if (!swapbytes)
        return (data[offset] << 8) | data[offset+1];

      return data[offset] | (data[offset+1] << 8);
    },

  read32:
    function (data, offset, swapbytes) {
      if (!swapbytes)
        return (data[offset] << 24) | (data[offset+1] << 16) | (data[offset+2] << 8) | data[offset+3];

      return data[offset] | (data[offset+1] << 8) | (data[offset+2] << 16) | (data[offset+3] << 24);
    },

  bytesToString:
    function (data, offset, num) {
      var s = "";

      for (var i=offset; i<offset+num; i++) {
        if (data[i] == 0)
          continue;

        s += String.fromCharCode(data[i]);
      }

      return s;
    },

  /* Converts from Decimal Degrees to Degrees Minutes Seconds.
   * Kept from the original code just in case.
   */
  dd2dms:
    function (gpsval) {
      // a bit unconventional calculation to get input edge cases
      // like 0x31 / 0x01, 0x0a / 0x01, 0x3c / 0x01 to 49°11'0" instead of 49°10'60"
      var gpsDeg = Math.floor(gpsval / 3600);
      gpsval -= gpsDeg * 3600.0;
      var gpsMin = Math.floor(gpsval / 60);
      // round to 2 digits after the comma
      var gpsSec = (gpsval - gpsMin * 60.0).toFixed(2);
      return new Array(gpsDeg, gpsMin, gpsSec);
    },

  dd2dd:
    function (gpsval) {
      // round to 6 digits after the comma
      var gpsArr = new Array();
      gpsArr.push((gpsval / 3600).toFixed(6));
      return gpsArr;
    },

  getDataStream:
    function (imgUrl) {
      var istream = null;

      try {
        if (!istream) {
          var ios = Components.classes["@mozilla.org/network/io-service;1"]
                              .getService(Components.interfaces.nsIIOService);
          var u = ios.newURI(imgUrl, null, null);
          // see if it's a local file and we can open it
          if (u.schemeIs("file")) {
            var fileHandler = ios.getProtocolHandler("file")
                                 .QueryInterface(Components.interfaces.nsIFileProtocolHandler);
            var f = fileHandler.getFileFromURLSpec(imgUrl);
            istream = Components.classes["@mozilla.org/network/file-input-stream;1"]
                                .createInstance(Components.interfaces.nsIFileInputStream);
            istream.init(f, 1, 0, false);
          } else {
            // no input stream and not a local file. oh well.
            // might be in the process of loading or just not
            // cached.
            return null;
          }
        }
      }
      catch(ex) {}

      return istream;
    },

  gatherData:
    function (imgUrl) {
      var dataObj = {};

      var istream = this.getDataStream(imgUrl);
      if (istream) {
        try {
          var bis = Components.classes["@mozilla.org/binaryinputstream;1"]
                              .createInstance(Components.interfaces.nsIBinaryInputStream);
          bis.setInputStream(istream);
          var swapbytes = false;
          var marker = bis.read16();
          var len;
          if (marker == this.SOI_MARKER) {
            marker = bis.read16();
            // reading SOS marker indicates start of image stream
            while (marker != this.SOS_MARKER && !this.exifDone) {
              // length includes the length bytes
              len = bis.read16() - 2;

              if (marker == this.APP1_MARKER) {
                // for EXIF the first 6 bytes should be 'Exif\0\0'
                var header = bis.readBytes(6);
                // Is it EXIF?
                if (header == 'Exif\0\0') {
                  // 8 byte TIFF header
                  // first two determine byte order
                  var exifData = bis.readByteArray(len - 6);

                  swapbytes = this.read16(exifData, 0, false) == this.INTEL_BYTE_ORDER;

                  // next two bytes are always 0x002A
                  // offset to Image File Directory (includes the previous 8 bytes)
                  var ifd_ofs = this.read32(exifData, 4, swapbytes);
                  try {
                    ggExif.readExifDir(dataObj, exifData, ifd_ofs, swapbytes);
                  } catch(ex) {
                    dump(ex + '\n');
                  }
                  this.exifDone = true;
                }
              } else {
                // read and discard data ...
                bis.readBytes(len);
              }

              marker = bis.read16();
            }
          }
          bis.close();
          bis = istream = null;
          this.exifDone = false;
        } catch(ex) {
          dump(ex + '\n');
        }
      }

      return dataObj;
    },

}
